"""
Copyright (c) 2022 Huawei Technologies Co.,Ltd.

openGauss is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:

          http://license.coscl.org.cn/MulanPSL2

THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details.
"""
"""
Case Type   : 硬件故障类--磁盘
Case Name   : 表空间超大导致磁盘空间占满
Create At   : 2020-10-15
Owner       : n@ningyali
Description :
    1、通过DFR工具注入节点，将磁盘空间写到剩余100M
    2、创建表空间，关联表空间创建table
    CREATE TABLESPACE tblspace RELATIVE LOCATION 'tblspace' MAXSIZE '8G';
    CREATE TABLE xxx TABLESPACE tblspace;
    3、使用客户端持续向表写入数据
    4、消除故障
Expect      :
    1、故障注入成功
    2、表空间&表创建成功
    3、磁盘空间满后，提示磁盘空间已满，数据写入失败
    4、故障消除后，数据写入成功
History     :
    Modified by @wan005 2021/11/12: 清除故障重启集群状态暂时异常，并完善环境清理
    Modified by wx1115623 2022/10/10:增加等待时间，增加stop步骤
"""

import unittest
import time
import os
from yat.test import Node
from yat.test import macro
from testcase.utils.Logger import Logger
from testcase.utils.Common import Common
from testcase.utils.CommonSH import CommonSH
from testcase.utils.Constant import Constant
from testcase.utils.CommonSH import RestartDbCluster


class BigTablespaceAndDiskFull(unittest.TestCase):
    primary_sh = CommonSH('PrimaryDbUser')
    standby1_sh = CommonSH('Standby1DbUser')
    standby2_sh = CommonSH('Standby2DbUser')
    nodes_tuple = ('PrimaryDbUser', 'Standby1DbUser', 'Standby2DbUser')

    @RestartDbCluster(*nodes_tuple)
    def setUp(self):
        self.log = Logger()
        self.log.info(f"-----{os.path.basename(__file__)} start-----")
        self.primary_root_node = Node(node='PrimaryRoot')
        self.primary_user_node = Node(node='PrimaryDbUser')
        self.standby1_root_node = Node(node='Standby1Root')
        self.standby1_user_node = Node(node='Standby1DbUser')
        self.standby2_root_node = Node(node='Standby2Root')
        self.com = Common()
        self.Constant = Constant()
        self.ts_location = 'test_tblspace_hardware011'
        self.ts_name = 'tblspace'
        self.t_name = 'test_hardware011'

        self.log.info("----获取主节点数据目录所在磁盘----")
        cmd = f"df -h {macro.DB_INSTANCE_PATH}"
        self.log.info(cmd)
        msg = self.primary_root_node.sh(cmd).result()
        self.log.info(msg)
        self.db_disk_name = msg.splitlines()[-1].split()[0].strip()

    def test_main(self):
        try:
            self.log.info("----创建表空间，关联表空间创建table----")
            sql_cmd = f'''
                    create tablespace {self.ts_name} 
                        relative location '{self.ts_location}' maxsize '8g';
                    create table {self.t_name}(
                        t_id integer, 
                        t_desc character(50), 
                        t_contant varchar(50)) tablespace {self.ts_name};
                    '''
            self.log.info(sql_cmd)
            msg = self.primary_sh.executDbSql(sql_cmd)
            self.log.info(msg)
            self.assertIn(self.Constant.CREATE_TABLE_SUCCESS, msg)

            self.log.info("----注入磁盘满故障----")
            cfe_command = f'rfile_full (diskname) values ({self.db_disk_name})'
            cfe_msg = self.com.cfe_inject(self.primary_root_node, cfe_command)
            self.log.info(cfe_msg)
            self.assertIn(self.Constant.CFE_DISK_FULL_SUCCESS_MSG, cfe_msg)

            self.log.info("----持续向表写入数据----")
            for i in range(1, 10000000):
                self.log.info(f"----第{i}次：向表写入数据----")
                sql_cmd = f'''insert into {self.t_name} values (
                    generate_series(1,10000), 
                    '测试{i}', 
                    '开始执行{time.strftime("%Y%m%d_%H:%M:%S", 
                                        time.localtime())}');'''
                self.log.info(sql_cmd)
                sql_result = self.primary_sh.executDbSql(sql_cmd)
                self.log.info(sql_result)

                self.log.info("----查看磁盘剩余可用空间----")
                avail_size = self.com.getAvailSize(self.primary_root_node,
                                                   macro.DB_INSTANCE_PATH)
                self.log.info('Available:' + avail_size)

                if self.Constant.DISK_FULL_MSG in sql_result or \
                        avail_size == '0':
                    self.log.info('磁盘已满')
                    break

            self.log.info('----磁盘空间满后，提示磁盘空间已满，数据写入失败----')
            sql_cmd = f'''insert into {self.t_name} values (
                10000001, 
                '磁盘空间满后写入', 
                '开始执行{time.strftime("%Y%m%d_%H:%M:%S", 
                                    time.localtime())}');'''
            self.log.info(sql_cmd)
            sql_result = self.primary_sh.executDbSql(sql_cmd)
            self.log.info(sql_result)
            # 磁盘满可能导致数据库主节点stop，继而连接数据库失败
            if self.Constant.FAILED_CONNECT_DB not in sql_result:
                self.assertIn(self.Constant.DISK_FULL_MSG, sql_result)
        finally:
            self.log.info("----清除故障----")
            cfe_command = f"rfile_full where (diskname={self.db_disk_name})"
            cfe_msg = self.com.cfe_clean(self.primary_root_node, cfe_command)
            self.log.info(cfe_msg)
            self.assertIn(self.Constant.CFE_DISK_CLEAN_SUCCESS_MSG, cfe_msg)

            self.log.info("----查看磁盘剩余可用空间----")
            avail_size = self.com.getAvailSize(self.primary_root_node,
                                               macro.DB_INSTANCE_PATH)
            self.log.info('Available:' + avail_size)
            self.assertTrue(avail_size != '0')

            self.log.info("----磁盘满可能导致数据库主节点stop，重启集群以恢复----")
            time.sleep(60)
            stop_msg = self.primary_sh.stopDbCluster()
            self.assertTrue(stop_msg)
            is_started = self.primary_sh.startDbCluster(True)
            self.log.info(is_started)
            flg = self.Constant.START_SUCCESS_MSG in is_started or \
                  "Please check manually" in is_started
            self.assertTrue(flg)
            if "Please check manually" in is_started:
                result = self.standby1_sh.buildStandby()
                self.assertIn(self.Constant.BUILD_SUCCESS_MSG, result)
                result = self.standby2_sh.buildStandby()
                self.assertIn(self.Constant.BUILD_SUCCESS_MSG, result)

            self.log.info('----查看主机query，同步是否正常----')
            self.primary_sh.check_location_consistency('primary', 3)
            self.assertTrue(self.primary_sh.getDbClusterStatus('status'))

            self.log.info("----清除故障后数据库可写入----")
            sql_cmd = f'''insert into {self.t_name} values (
                1000002, 
                '清除故障后写入', 
                '开始执行{time.strftime("%Y%m%d_%H:%M:%S", 
                                    time.localtime())}');'''
            self.log.info(sql_cmd)
            msg = self.primary_sh.executDbSql(sql_cmd)
            self.log.info(msg)
            for error_msg in self.Constant.SQL_WRONG_MSG:
                self.assertNotIn(error_msg, msg)

    def tearDown(self):
        self.log.info("----清除故障----")
        cfe_command = f"rfile_full where (diskname={self.db_disk_name})"
        cfe_msg = self.com.cfe_clean(self.primary_root_node, cfe_command)
        self.log.info(cfe_msg)
        self.assertIn(self.Constant.CFE_DISK_CLEAN_SUCCESS_MSG, cfe_msg)
        time.sleep(3)
        is_started = self.primary_sh.startDbCluster(True)
        self.log.info(is_started)
        time.sleep(3)
        self.log.info('----清理环境，删除表，表空间----')
        drop_table_cmd = f'drop table if exists {self.t_name}; ' \
            f'drop tablespace {self.ts_name}'
        self.log.info(drop_table_cmd)
        msg = self.primary_sh.executDbSql(drop_table_cmd)
        self.log.info(msg)

        self.log.info('----删除表空间目录----')
        shell_cmd = f"rm -rf {self.ts_location}"
        self.log.info(shell_cmd)
        msg = self.primary_root_node.sh(shell_cmd).result()
        self.log.info(msg)
        msg = self.standby1_root_node.sh(shell_cmd).result()
        self.log.info(msg)
        msg = self.standby2_root_node.sh(shell_cmd).result()
        self.log.info(msg)
        self.log.info(f"-----{os.path.basename(__file__)} end-----")
